////////////////////////////////////////////////
// -- Inscript                                //
// by Hurricaaane (Ha3)                       //
//                                            //
// http://www.youtube.com/user/Hurricaaane    //
//--------------------------------------------//
// TPlugin Module                             //
////////////////////////////////////////////////

module( "inscript_plugin", package.seeall )

local Plugins = {}
local Plugins_Names = {}


local PLUGIN = {}

function PLUGIN:GetRawName()
	return self._rawname
end

function PLUGIN:GetDisplayName()
	return self.Name
end

function PLUGIN:IsMounted()
	return self._IsMounted
end

function PLUGIN:IsPerform()
	return self._IsPerform
end

function PLUGIN:Mount()
	if self._IsMounted then return false end
	
	if not self._IsPerform then
		print(INSCRIPT_NAME .. " >> Mounting Plugin \"".. self:GetDisplayName() .."\" ...")
		
	else
		print(INSCRIPT_NAME .. " >> Performing Plugin \"".. self:GetDisplayName() .."\".")
		
	end
	
	if self.Load then
		local bOkay, strErr = pcall(function() self:Load() end)
		if not bOkay then print(" > " .. INSCRIPT_NAME .. " MountPlugin ERROR [".. self._rawname .."] : ".. strErr) end
	end
	
	if not self._IsPerform then
		
		for hookName, func in pairs(self.Hooks) do
			hook.Add( hookName , "inscript_" .. self._rawname .. "_" .. hookName , func )
			if INSCRIPT_DEBUG then print(INSCRIPT_NAME .. " >> Hooked " .. "inscript_" .. self._rawname .. "_" .. hookName ..".") end
		end
		
		self._IsMounted = true
		
		self:FlushChanges()
	end
end

function PLUGIN:Unmount()
	if not self._IsMounted then return false end
	
	print(INSCRIPT_NAME .. " >> Unmounting Plugin [".. self:GetDisplayName() .."].")
	
	--table.Empty(self.Parameters)
	--table.Empty(self.Parameters_Names)
	
	for hookName, func in pairs(self.Hooks) do
		hook.Remove( hookName , "inscript_" .. self._rawname .. "_" .. hookName)
	end
	
	if self.Unload then
		local bOkay, strErr = pcall(function() self:Unload() end)
		if not bOkay then print(" > " .. INSCRIPT_NAME .. " UnmountPlugin ERROR [".. self._rawname .."] : ".. strErr) end
	end
	
	--self:DestroyMenu()
	
	self._IsMounted = false
end

function PLUGIN:AddParameter( sParameterName, stData, optbShouldWatch )
	if stData.Type == nil or stData.Type == "" then return end

	sParameterName = string.lower(sParameterName)
	stData.Type = string.lower(stData.Type)
	if self.Parameters[sParameterName] then return end
	if not inscript.ParamTypeExists( stData.Type ) then return end
	
	stData._plugin = self
	self.Parameters[sParameterName] = stData
	table.insert(self.Parameters_Names, sParameterName)
	
	if INSCRIPT_DEBUG then print(INSCRIPT_NAME .. " > Adding Parameters : "..sParameterName.." ["..stData.Type.."]") end
	local doneconvar = inscript.BuildParamConvars( stData.Type, "inscript_parameter_".. self:GetRawName() .."_".. sParameterName, stData.Defaults )
	
	if optbShouldWatch and not self._IsPerform and doneconvar then
		if not self._WatchParameters then
			self._WatchParameters = {}
			self._WatchParametersCache = {}
		end
		
		self._WatchParameters[sParameterName] = false
		self._WatchParametersCache[sParameterName] = self:GetParameterSettings( sParameterName )
		
		if INSCRIPT_DEBUG then print(INSCRIPT_NAME .. " > Added Watch on "..sParameterName..".") end
	end
end

function PLUGIN:GetParametersNames()
	return self.Parameters_Names
end

function PLUGIN:BuildParameterPanel( sParamName )
	local sFullConvarName = "inscript_parameter_".. self:GetRawName() .."_".. sParamName
	return inscript.BuildParamPanel( sFullConvarName , self.Parameters[sParamName] )
end

function PLUGIN:GetParameterSettings( sParameterName, optbShouldUseString )
	if not self.Parameters[sParameterName] then
		print("> " .. INSCRIPT_NAME .. " Parameter ERROR : Requested field ".. sParameterName .. " that doesn't exist !")
		return 0
	end

	local sFullConvarName = "inscript_parameter_".. self:GetRawName() .."_".. sParameterName
	local sType = self.Parameters[sParameterName].Type
	
	self.TEMP_myValues = inscript.GetParamConvars( sType, sFullConvarName )
	for k,v in pairs( self.TEMP_myValues ) do
		self.TEMP_myValues[k] = inscript.GetVar( v, optbShouldUseString )
	end
	return (#self.TEMP_myValues == 1) and self.TEMP_myValues[1] or self.TEMP_myValues
end

function PLUGIN:GetNumber( sParameterName )
	local myParam = self:GetParameterSettings( sParameterName )
	
	if (type(myParam) == "table") then
		return myParam[1]
	end
	return tonumber(myParam)
end

function PLUGIN:GetString( sParameterName )
	local myParam = self:GetParameterSettings( sParameterName, true )
	
	if (type(myParam) == "table") then
		return myParam[1]
	end
	return tostring(myParam)
end

function PLUGIN:FlushParameter( sParameterName )
	if not self._WatchParameters then return end
	
	if self._WatchParameters[sParameterName] then
		self._WatchParameters[sParameterName] = false
		self._WatchParametersCache[sParameterName] = self:GetParameterSettings( sParameterName, true )
	
	end
end

function PLUGIN:FlushChanges()
	if not self._WatchParameters then return end
	
	for sParameterName,v in pairs(self._WatchParameters) do
		self:FlushParameter( sParameterName )
	end

end

function PLUGIN:HasChanged( sParameterName )
	if not self._WatchParameters then return false end
	
	return self._WatchParameters[sParameterName] or false
end

function PLUGIN:GetPluginConvarTable()
	local myFullConvars = {}
	for k,sParameterName in pairs( self.Parameters_Names ) do
		local sType = self.Parameters[sParameterName].Type
		local sFullConvarName = "inscript_parameter_".. self:GetRawName() .."_".. sParameterName
		
		table.Add( myFullConvars, inscript.GetParamConvars( sType, sFullConvarName ) )
	end
	
	return myFullConvars
end

function PLUGIN:GetPluginDefaultsTable()
	local Defaults = {}
	for k,sParameterName in pairs( self.Parameters_Names ) do
		local sType = self.Parameters[sParameterName].Type
		local sFullConvarName = "inscript_parameter_".. self:GetRawName() .."_".. sParameterName
		
		local stSuffixes = inscript.GetParamSuffixes( sType )
		local ttDefaults = self.Parameters[sParameterName].Defaults
		
		if stSuffixes == nil then
			Defaults[sFullConvarName] = tostring(ttDefaults)
			
		elseif type(stSuffixes) == "table" then
			for k,sSuffix in pairs( stSuffixes ) do
				Defaults[sFullConvarName .. "_" .. sSuffix] = tostring(ttDefaults[k] or ttDefaults[1] or ttDefaults)
			end
		end
		
	end
	
	return Defaults
end

function PLUGIN:BuildMenu()
	if self.DermaPanel then self.DermaPanel:Remove() end

	self.DermaPanel = inscript.Util_MakeFrame( 244, ScrH() * 0.5, self:GetDisplayName() )
	self.DermaPanel:SetTitle( self:GetDisplayName() )
	local refPanel = self.DermaPanel
	
	inscript.Util_MakeCategory( refPanel, "General", 0 )
	inscript.Util_AppendCheckBox( refPanel, "Enable as Auto-mount", "inscript_plugin_" .. self:GetRawName() )
	
	inscript.Util_AppendLabel( refPanel, "Call this menu using :" )
	do
		local MenuCommand = vgui.Create("DTextEntry")
		MenuCommand:SetText( "inscript_param_" .. self:GetRawName() )
		MenuCommand:SetEditable( false )
		inscript.Util_AppendPanel( refPanel, MenuCommand )
	end
	inscript.Util_AppendLabel( refPanel, "Perform the script using :" )
	do
		local MenuCommand = vgui.Create("DTextEntry")
		MenuCommand:SetText( "inscript_call_mountplugin " .. self:GetRawName() )
		MenuCommand:SetEditable( false )
		inscript.Util_AppendPanel( refPanel, MenuCommand )
	end
	
	do
		local MButton = vgui.Create("DButton")
		local plugin = self
		
		local isPerform = plugin:IsPerform()
		local seemsEnabled = isPerform or plugin:IsMounted()
		MButton:SetText( isPerform and "Perform" or (seemsEnabled and "Unmount" or "Mount") )
		
		--local wasMounted = self:IsMounted()
		
		MButton.DoClick = function()			
			local isEnabled = plugin:IsMounted()
			
			if not isEnabled then plugin:Mount()
			else 				  plugin:Unmount() end
			
			if not plugin:IsPerform() then
				MButton:SetText( (not isEnabled) and "Unmount" or "Mount" )
			end
			--[[if not wasMounted then
				local myExpand = inscript.Util_FrameGetExpandTable( refPanel )
				
				self:BuildMenu( myExpand )
				self:ShowMenu()
				
			else
				MButton:SetText( (not isEnabled) and "Unmount" or "Mount" )
				
			end]]--
			
		end
		
		inscript.Util_AppendPanel( refPanel, MButton )
	end
	
	if self.Description then
		inscript.Util_MakeCategory( refPanel, "Description", 0 )
		inscript.Util_AppendLabel( refPanel, tostring(self.Description), true)
		
	end
	
	inscript.Util_MakeCategory( refPanel, "Parameters", 1 )
	
	--if self:IsMounted() then		
		local paramNames = self:GetParametersNames()
		for k,sName in pairs(paramNames) do
			local myPanel = self:BuildParameterPanel( sName )
			inscript.Util_AppendPanel( refPanel, myPanel )
		end
		
		if #paramNames == 0 then
			inscript.Util_AppendLabel( refPanel, "No parameters to set." )
		end
		
	--end
	
	inscript.Util_ApplyCategories( refPanel )
end

function PLUGIN:ShowMenu()
	if not self.DermaPanel then
		self:BuildMenu()
	end
	self.DermaPanel:MakePopup()
	self.DermaPanel:SetVisible( true )
end

function PLUGIN:DestroyMenu()
	if self.DermaPanel then
		self.DermaPanel:Remove()
		self.DermaPanel = nil
	end
end

local inscript_plugin_meta = {__index=PLUGIN}


function IsValidHookName( sFunctionName )
	return (string.Left( sFunctionName , 1 ) ~= "_") and (sFunctionName ~= "Load") and (sFunctionName ~= "Unload") and (sFunctionName ~= "LoadParameters")
end

function Register( sName, dPlugin )
	if Plugins[sName] then return end
	
	if INSCRIPT_DEBUG then print(INSCRIPT_NAME .. " >> Registering Plugin ["..sName.."]") end
	
	dPlugin.Parameters = {}
	dPlugin.Parameters_Names = {}

	dPlugin._IsMounted = false
	dPlugin._IsPerform = dPlugin.Trigger or false
	dPlugin._WatchParameters      = nil
	dPlugin._WatchParametersCache = nil
	
	dPlugin._rawname = sName
	dPlugin.Name = dPlugin.Name or sName
	
	dPlugin.Hooks = {}
	if not dPlugin._IsPerform then
		--CAPSADMIN : Remove this part
		--[[for name, func in pairs( dPlugin ) do
			if type(func) == "function" and IsValidHookName( name ) then
				dPlugin.Hooks[name] = function(...) return func( dPlugin, ... ) end
			end
		end]]--
		--NIMDASPAC
		
		for name, func in pairs( dPlugin.HOOK ) do
			if type(func) == "function" then
				dPlugin.Hooks[name] = function(...) return func( dPlugin, ... ) end
			end
		end
	end
	
	local sCvarPrefix = "inscript_plugin_" .. dPlugin._rawname
	inscript.CreateVar(sCvarPrefix, ( dPlugin.DefaultOn or false ) and "1" or "0", true, false)
	
	local sCvarBarnstar = "inscript_barnstar_" .. dPlugin._rawname
	inscript.CreateVar(sCvarBarnstar, "0", true, false)
	
	concommand.Add("inscript_param_" .. dPlugin._rawname, function() dPlugin:ShowMenu() end )
	
	setmetatable(dPlugin, inscript_plugin_meta)
	
	Plugins[sName] = dPlugin
	table.insert(Plugins_Names, sName)
	

	if dPlugin.LoadParameters then
		local bOkay, strErr = pcall(function() dPlugin.LoadParameters( dPlugin ) end)
		if not bOkay then print(" > " .. INSCRIPT_NAME .. " LoadParameters ERROR [".. dPlugin._rawname .."] : ".. strErr) end
	end
end

function GetAllPlugins( )
	return Plugins
end

function GetPluginObject( sName )
	if not Plugins[sName] then return end
	
	return Plugins[sName]
end

function SortNamesTable()
	if #Plugins_Names > 2 then // Strictly superior to 2
		table.sort(Plugins_Names, function(a,b)
			return Plugins[a]:GetDisplayName()
				 < Plugins[b]:GetDisplayName()
		end)
	end
end

function ForthBarnstars()
	local i = #Plugins_Names
	
	for k=1,#Plugins_Names do
		if ((inscript.GetVar("inscript_barnstar_" .. Plugins_Names[i]) or 0) > 0) then
			table.insert(Plugins_Names, 1, Plugins_Names[i])
			table.remove(Plugins_Names, i + 1)
		else
			i = i - 1
			
		end
	end
end

function GetNamesTable()
	return table.Copy( Plugins_Names )
end

function RemoveAll()
	for k,plugin in pairs(Plugins) do
		plugin:Unmount()
		table.Empty(plugin.Parameters)
		table.Empty(plugin.Parameters_Names)
		concommand.Remove("inscript_param_" .. plugin._rawname )
	end
	
	
	table.Empty(Plugins_Names)
	table.Empty(Plugins)
end
